package br.usp.pcs.basys2.file;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import br.usp.pcs.basys2.BasysProcess;
import br.usp.pcs.basys2.info.FileActivityInfo;
import br.usp.pcs.basys2.info.FileInfo;
import br.usp.pcs.basys2.info.FileActivityInfo.Mode;

public class BasysFile {

	public final class AcessInfo {
		private BasysProcess process;
		private FileActivityInfo activityInfo;

		public BasysProcess getProcess() {
			return process;
		}

		public AcessInfo(BasysProcess process, FileActivityInfo activityInfo) {
			super();
			this.process = process;
			this.activityInfo = activityInfo;
		}

		public FileActivityInfo getActivityInfo() {
			return activityInfo;
		}
	}

	private Queue<AcessInfo> blockedprocessesQueue = new LinkedList<AcessInfo>();
	private Queue<AcessInfo> processesQueue = new LinkedList<AcessInfo>();
	private BasysProcess accessingProcess = null;
	private boolean writehasBeenRequested = false;
	private List<FileManagerListener> listeners = new ArrayList<FileManagerListener>();
	private FileInfo info;

	public BasysFile(FileInfo info) {
		super();
		this.info = info;
	}

	public boolean add(FileManagerListener e) {
		return listeners.add(e);
	}

	public void requestAcess(BasysProcess process, FileActivityInfo activityInfo) {
		if (writehasBeenRequested) {
			blockedprocessesQueue.add(new AcessInfo(process, activityInfo));
		} else {
			if (fileIsFree()) {
				startAccess(new AcessInfo(process, activityInfo));
			} else {
				processesQueue.add(new AcessInfo(process, activityInfo));
			}
		}
	}

	private void startAccess(AcessInfo acessInfo) {
		accessingProcess = acessInfo.getProcess();
		if (acessInfo.getActivityInfo().getMode() == Mode.WRITE) {
			writehasBeenRequested = true;
		}

		for (FileManagerListener listener : listeners) {
			listener.onProcesStartedAccessingFile(acessInfo);
		}
	}

	public void endAccess(AcessInfo acessInfo) {
		if (acessInfo.getActivityInfo().getMode() == Mode.WRITE) {
			writehasBeenRequested = false;
			if (!blockedprocessesQueue.isEmpty()) {
				AcessInfo poll = blockedprocessesQueue.poll();
				processesQueue.add(poll);
				Mode nextMode = poll.getActivityInfo().getMode();
				if (nextMode == Mode.READ) {
					while ((!blockedprocessesQueue.isEmpty())
							&& blockedprocessesQueue.peek().getActivityInfo()
									.equals(nextMode)) {
						processesQueue.add(blockedprocessesQueue.poll());
					}
				}
			}
		}
		for (FileManagerListener listener : listeners) {
			listener.onProcesStopedAccessingFile(acessInfo);
		}
		if (!processesQueue.isEmpty()) {
			startAccess(processesQueue.poll());
		} else {
			accessingProcess = null;
		}
	}

	public boolean fileIsFree() {
		boolean result;
		if (writehasBeenRequested) {
			result = false;
		} else {
			result = true;
		}
		return result;
	}

	public BasysProcess getAccessingProcess() {
		return accessingProcess;
	}

	public String getName() {
		return info.getName();
	}

}
